<?php

namespace SunAdmin\Service\Ws;

use think\swoole\Websocket;
use think\swoole\websocket\Event;
use think\facade\Log;
use think\Exception;

class SendMessage extends BaseWebSocketService
{
    private Websocket $websocket;
    private FdUidBinding $fdUidBinding;

    public function __construct(string $connectionType)
    {
        parent::__construct($connectionType);

        $this->websocket = app(Websocket::class);
        $this->fdUidBinding = new FdUidBinding($connectionType);
    }

    /**
     * 通过uid发送消息
     */
    public function toUidSendMsg(int $uid, Event $event)
    {
        try {
            $fds = $this->fdUidBinding->uidToFds($uid);
            if (empty($fds)) {
                Log::warning("[webSocket] 用户不在线，无法发送消息 | " . json_encode([
                        'type' => $this->connectionType,
                        'uid' => $uid,
                        'event_type' => $event->type
                    ]));
            }
            $this->toFdSendMsg($fds, $event);
        } catch (\Throwable $e) {
            Log::error("[webSocket] 通过uid发送消息失败 | " . json_encode([
                    'type' => $this->connectionType,
                    'uid' => $uid,
                    'event_type' => $event->type ?? 'unknown',
                    'error' => $e->getMessage()
                ]));
        }
        return true;
    }

    /**
     * 通过fd发送消息
     */
    public function toFdSendMsg($fds, Event $event): bool
    {
        if (!is_array($fds)) {
            $fds = [$fds];
        }
        foreach ($fds as $fd) {
            try {
                $this->websocket->to($fd)->push($event);
            } catch (\Throwable $e) {
                Log::error("[webSocket] 通过fd发送消息失败 | " . json_encode([
                        'type' => $this->connectionType,
                        'fd' => $fd,
                        'event_type' => $event->type ?? 'unknown',
                        'error' => $e->getMessage()
                    ]));
            }
        }
        return true;
    }

    /**
     * 发送群组消息
     */
    public function toGroupSendMsg(array $uids, Event $event): array
    {
        $results = [
            'success_uids' => [],
            'failed_uids' => [],
            'total_count' => count($uids),
            'success_count' => 0
        ];

        try {
            foreach ($uids as $uid) {
                $success = $this->toUidSendMsg($uid, $event);
                if ($success) {
                    $results['success_uids'][] = $uid;
                    $results['success_count']++;
                } else {
                    $results['failed_uids'][] = $uid;
                }
            }
        } catch (\Throwable $e) {
            Log::error("[webSocket] 群组消息发送异常 | " . json_encode([
                    'type' => $this->connectionType,
                    'event_type' => $event->type ?? 'unknown',
                    'error' => $e->getMessage()
                ]));
        }

        return $results;
    }

    /**
     * 广播消息给所有在线用户
     */
    public function broadcastToAll(Event $event): bool
    {
        try {
            $onlineUids = $this->fdUidBinding->getAllOnlineUids();

            if (empty($onlineUids)) {
                Log::info("[webSocket] 没有在线用户，跳过广播 | " . json_encode([
                        'type' => $this->connectionType,
                        'event_type' => $event->type ?? 'unknown'
                    ]));
                return true;
            }

            $results = $this->toGroupSendMsg($onlineUids, $event);

            return $results['success_count'] > 0;

        } catch (\Throwable $e) {
            Log::error("[webSocket] 广播消息失败 | " . json_encode([
                    'type' => $this->connectionType,
                    'event_type' => $event->type ?? 'unknown',
                    'error' => $e->getMessage()
                ]));
            return false;
        }
    }

    /**
     * 广播消息给所有在线用户（排除指定用户）
     */
    public function broadcastToAllExcept(Event $event, array $excludeUids = []): bool
    {
        try {
            $onlineUids = $this->fdUidBinding->getAllOnlineUids();
            $targetUids = array_diff($onlineUids, $excludeUids);

            if (empty($targetUids)) {
                Log::info("[webSocket] 没有目标用户，跳过发送 | " . json_encode([
                        'type' => $this->connectionType,
                        'event_type' => $event->type ?? 'unknown',
                        'excluded_count' => count($excludeUids),
                        'online_count' => count($onlineUids)
                    ]));
                return true;
            }

            $results = $this->toGroupSendMsg($targetUids, $event);

            return $results['success_count'] > 0;

        } catch (\Throwable $e) {
            Log::error("[webSocket] 排除指定用户的广播消息失败 | " . json_encode([
                    'type' => $this->connectionType,
                    'event_type' => $event->type ?? 'unknown',
                    'excluded_uids' => $excludeUids,
                    'error' => $e->getMessage()
                ]));
            return false;
        }
    }

    /**
     * 强制用户下线
     */
    public function forceUserOffline(int $uid, string $reason = '系统强制下线'): bool
    {
        try {
            $fds = $this->fdUidBinding->uidToFds($uid);

            if (empty($fds)) {
                Log::info("[webSocket] 用户已离线，无需强制下线 | " . json_encode([
                        'type' => $this->connectionType,
                        'uid' => $uid
                    ]));
                return true;
            }

            // 发送下线通知
            $message = new Event('forceOffline', [
                'reason' => $reason,
                'timestamp' => time()
            ]);

            $this->toFdSendMsg($fds, $message);

            // 延迟断开连接，确保消息发送完成
            swoole_timer_after(1000, function () use ($fds, $uid) {
                foreach ($fds as $fd) {
                    try {
                        $this->websocket->close($fd);
                    } catch (\Throwable $e) {
                        Log::warning("[webSocket] 强制断开连接失败 | " . json_encode([
                                'type' => $this->connectionType,
                                'fd' => $fd,
                                'uid' => $uid,
                                'error' => $e->getMessage()
                            ]));
                    }
                }

                // 清理绑定
                $this->fdUidBinding->unbindByUid($uid);
            });

            Log::info("[webSocket] 用户强制下线成功 | " . json_encode([
                    'type' => $this->connectionType,
                    'uid' => $uid,
                    'reason' => $reason,
                    'connections_count' => count($fds)
                ]));

            return true;

        } catch (\Throwable $e) {
            Log::error("[webSocket] 强制用户下线失败 | " . json_encode([
                    'type' => $this->connectionType,
                    'uid' => $uid,
                    'reason' => $reason,
                    'error' => $e->getMessage()
                ]));
            return false;
        }
    }

    /**
     * 检查消息发送权限
     */
    private function checkSendPermission(int $senderUid, string $messageType): bool
    {
        try {
            // 客户端用户只能发送特定类型的消息
            if ($this->connectionType === 'client') {
                $allowedTypes = ['ping', 'chat', 'status']; // 客户端允许的消息类型
                if (!in_array($messageType, $allowedTypes)) {
                    Log::warning("[webSocket] 客户端无权发送此类型消息 | " . json_encode([
                            'type' => $this->connectionType,
                            'sender_uid' => $senderUid,
                            'message_type' => $messageType
                        ]));
                    return false;
                }
            }

            return true;

        } catch (\Throwable $e) {
            Log::error("[webSocket] 检查消息发送权限失败 | " . json_encode([
                    'type' => $this->connectionType,
                    'sender_uid' => $senderUid,
                    'message_type' => $messageType,
                    'error' => $e->getMessage()
                ]));
            return false;
        }
    }
} 